package com.jl.interceptor;

import com.jl.JLReflect;
import com.jl.JLTuple;
import com.jl.interceptor.annotate.JLInterceptor;
import com.jl.interceptor.error.JLInrerceptorError;
import com.jl.set.list.JArrayList;
import com.jl.set.list.JList;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * 拦截器
 */
@Slf4j
@Component
public class JLInterceptorImpl extends HandlerInterceptorAdapter implements WebMvcConfigurer {

    @Autowired
    private ApplicationContext applicationContext;

    private JList<Interceptor> list;

    /**
     * 方法执行前
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (handler.getClass().isAssignableFrom(HandlerMethod.class)) {
            HandlerMethod handler2 = (HandlerMethod) handler;
            //这里判断异常的不重复执行拦截器
            if (BasicErrorController.class.equals(handler2.getBeanType())) {
                return true;
            }
        }
        //这里判断静态资源的不重复执行拦截器
        if (handler.getClass().isAssignableFrom(ResourceHttpRequestHandler.class)) {
            return true;
        }
        return exec(request, 0);
    }

    /**
     * 方法执行后
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        exec(request, 1);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this)
                .addPathPatterns("/**");
        cache();
    }

    /**
     * 执行拦截器逻辑
     */
    private boolean exec(HttpServletRequest request, int type) {
        for (Interceptor interceptor : list) {
            JLInterceptor jlInterceptor = interceptor.getJlInterceptor();
            if ((type == 0 && jlInterceptor.before()) || (type == 1 && jlInterceptor.after())) {
                String[] excludes = jlInterceptor.excludes();
                List<String> excludesList = Arrays.asList(excludes);
                if (excludesList.contains(request.getRequestURI())) {
                    continue;
                }
                Object bean = interceptor.getBean();
                Method method = interceptor.getMethod();
                Object[] value = jlInterceptor.values();
                Object[] param = JLReflect.MethodReflect.checkParamType(method.getParameters(), value);
                Object invoke = null;
                try {
                    invoke = method.invoke(bean, param);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                if (invoke != null && invoke instanceof Boolean) {
                    if (!Boolean.parseBoolean(invoke.toString())) {
                        throw new JLInrerceptorError(jlInterceptor.error());
                    }
                }
            }
        }
        return true;
    }

    /**
     * 缓存
     */
    private void cache() {
        JList<Interceptor> jList = new JArrayList<>();
        String[] beans = applicationContext.getBeanDefinitionNames();
        for (String bean : beans) {
            Object beanObj;
            try {
                beanObj = applicationContext.getBean(bean);
            } catch (Exception e) {
                continue;
            }
            List<JLTuple.Tuple2<Method, JLInterceptor>> methods = JLReflect.MethodReflect.getMethod(beanObj.getClass(), JLInterceptor.class);
            for (JLTuple.Tuple2<Method, JLInterceptor> methodObj : methods) {
                Method method = methodObj.getV1();
                JLInterceptor jlInterceptor = methodObj.getV2();
                int order = jlInterceptor.order();
                Interceptor interceptor = Interceptor.builder()
                        .bean(beanObj)
                        .method(method)
                        .order(order)
                        .jlInterceptor(jlInterceptor)
                        .build();
                jList.add(interceptor);
            }
        }
        list = jList.asc(Interceptor::getOrder);
    }

    @Data
    @Builder
    public static class Interceptor {
        private Object bean;

        private Method method;

        private int order;

        private JLInterceptor jlInterceptor;
    }

}
